from copy import deepcopy

from utils.copy import smartcopy, fetch_copy, deepcopy_into
from utils.interval import Interval


class Warehouse(object):
    """This warehouse class contains part of the data relevant for the CS problem."""
    def __init__(self, xlimits=None, ylimits=None, cranes=()):
        self.cranes = set()              # {Crane}
        self.id_to_crane = dict()        # {crane_id: Crane}
        self.itemid_to_craneid = dict()  # {item_id: crane_id}
        self.xlimits = None if xlimits is None else Interval(*xlimits)
        self.ylimits = None if ylimits is None else Interval(*ylimits)
        for crane in cranes:
            self.add(crane)
            
    def __getitem__(self, key):
        return self.id_to_crane[key]
        
    def __deepcopy__(self, memo):
        clone = smartcopy(self, memo, deepcopy_attrs=("xlimits", "ylimits"))
        clone.cranes = set()
        clone.id_to_crane = dict()
        clone.itemid_to_craneid = dict()
        for crane in self.cranes:
            clone.add(deepcopy(crane, memo))
        return clone
        
    deepcopy = deepcopy_into
    
    def _get(self):
        return self.id_to_crane
        
    def _add(self, crane):
        if crane.id in self.id_to_crane:
            raise KeyError("duplicate crane id - %s" % crane.id)
        self.id_to_crane[crane.id] = crane
        self.cranes.add(crane)
        
    def _remove(self, crane):
        if self.id_to_crane[crane.id] is not crane:
            raise ValueError("object mismatch (same id but different object)")
        del self.id_to_crane[crane.id]
        self.cranes.remove(crane)
        
    def get_location(self, item_id):
        return self.itemid_to_craneid.get(item_id, None)
        
    def set_location(self, item_id, crane_id):
        if crane_id is None:
            del self.itemid_to_craneid[item_id]
        else:
            if item_id in self.itemid_to_craneid:
                raise Exception("duplicate item location")
            self.itemid_to_craneid[item_id] = crane_id
            
            
class Component(object):
    """A warehouse component, i.e. an object which may be added/removed from a warehouse by means 
    of Warehouse.add() and Warehouse.remove(). Crane derives from this class."""
    def __init__(self, id, warehouse=None):
        self.id = id
        self.warehouse = None
        
    def _set_warehouse(self, warehouse):
        self.warehouse = warehouse
        
    def _get_warehouse(self):
        return self.warehouse
        
    def __deepcopy__(self, memo):
        clone = fetch_copy(self, memo)
        clone.id = self.id
        clone.warehouse = None
        return clone
        
    deepcopy = deepcopy_into
    
    
# create "duplex" Warehouse.add/remove() and Component.set_warehouse()
from utils.relations import relation, One, Many, METHODS

relation(One(Warehouse, simplex=METHODS, duplex=METHODS), 
         Many(Component, simplex=METHODS, duplex=METHODS, name=""))
